package com.novel.captcha.adapter;

import cn.hutool.core.util.IdUtil;
import com.novel.captcha.CaptchaCode;
import com.novel.captcha.cache.CaptchaCache;
import com.novel.captcha.cache.manager.CaptchaCacheManager;
import com.novel.captcha.config.CaptchaProperties;
import com.novel.captcha.exception.CaptchaException;
import com.novel.captcha.exception.CaptchaIsEmptyException;
import com.novel.captcha.exception.CaptchaRenderException;
import com.novel.captcha.exception.CaptchaTimeoutException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;

/**
 * 默认随机验证码实现类
 *
 * @author novel
 * @since 2023/8/3 18:14
 */
@AllArgsConstructor
@Slf4j
public abstract class AbstractCaptchaImpl implements Captcha {

    /**
     * 验证码缓存管理器
     */
    protected final CaptchaCacheManager captchaCacheManager;
    /**
     * 验证码配置
     */
    protected final CaptchaProperties captchaProperties;

    @Override
    public String render(@NotNull String key) {
        try {
            var captcha = getCaptcha();
            captcha.setWidth(captchaProperties.getWidth());
            captcha.setHeight(captchaProperties.getHeight());
            captcha.setLen(captchaProperties.getLength());
            captcha.setCharType(captchaProperties.getType().getType());
            String capText = captcha.text();
            CaptchaCache cache = captchaCacheManager.getCache((CaptchaProperties.PREFIX + "_" + this.captchaProperties.getType()).toUpperCase());
            cache.saveCaptcha(key, capText);
            return captcha.toBase64();
        } catch (Exception e) {
            throw new CaptchaRenderException();
        }
    }

    @Override
    public CaptchaCode render() {
        String uuid = IdUtil.simpleUUID();
        String captcha = render(uuid);
        CaptchaCode captchaCode = new CaptchaCode();
        captchaCode.setBase64Image(captcha);
        captchaCode.setKey(uuid);
        return captchaCode;
    }

    @Override
    public String writeImageCode(HttpServletResponse response) {
        CaptchaCode captchaCode = render();
        ServletOutputStream out = null;
        try {
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("key", captchaCode.getKey());
            response.setContentType("image/jpeg");

            out = response.getOutputStream();

            ImageIO.write(captchaCode.createImage(), "jpg", out);
            out.flush();
        } catch (Exception e) {
            throw new CaptchaException(e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ignored) {
            }
        }
        return captchaCode.getKey();
    }

    @Override
    public CaptchaCode writeBase64Code() {
        return render();
    }


    @Override
    public boolean validate(@NotNull String key, String code) {
        if (!StringUtils.hasLength(code) || !StringUtils.hasLength(code.trim())) {
            //需要验证的验证码不存在，用户未输入
            throw new CaptchaIsEmptyException();
        }
        String captchaCode = getCaptchaCode(key);

        if (captchaProperties.isCaseSensitivity()) {
            //区分大小写
            return captchaCode.equals(code);
        } else {
            //不区分大小写
            return captchaCode.equalsIgnoreCase(code);
        }
    }

    @Override
    public String getCaptchaCode(@NotNull String key) {
        CaptchaCache cache = captchaCacheManager.getCache((CaptchaProperties.PREFIX + "_" + this.captchaProperties.getType()).toUpperCase());
        String captcha = cache.getCaptcha(key);
        cache.removeCaptcha(key);
        if (!StringUtils.hasLength(captcha)) {
            //验证码不存在，已经过期
            throw new CaptchaTimeoutException();
        }
        return captcha;
    }
}
